package com.tianji.learning.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.tianji.api.client.course.CourseClient;
import com.tianji.api.client.learning.LearningClient;
import com.tianji.api.dto.course.CourseFullInfoDTO;
import com.tianji.api.dto.leanring.LearningLessonDTO;
import com.tianji.common.exceptions.DbException;
import com.tianji.common.utils.AssertUtils;
import com.tianji.common.utils.ObjectUtils;
import com.tianji.learning.domain.dto.LearningRecordFormDTO;

import com.tianji.api.dto.leanring.LearningRecordDTO;
import com.tianji.common.utils.BeanUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.domain.enums.LessonStatus;
import com.tianji.learning.domain.enums.SectionType;
import com.tianji.learning.domain.po.LearningLesson;
import com.tianji.learning.domain.po.LearningRecord;
import com.tianji.learning.mapper.LearningRecordMapper;
import com.tianji.learning.service.ILearningLessonService;
import com.tianji.learning.service.ILearningRecordService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.learning.utils.LearningRecordDelayTaskHandler;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.jni.Local;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

import static com.tianji.learning.domain.enums.LessonStatus.*;
import static com.tianji.learning.domain.enums.SectionType.VIDEO;

/**
 * 学习记录表 服务实现类
 *
 * @author author
 * @since 2025-03-21
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class LearningRecordServiceImpl extends ServiceImpl<LearningRecordMapper, LearningRecord> implements ILearningRecordService {

    private final ILearningLessonService lessonService;
    private final CourseClient courseClient;
    private final LearningRecordDelayTaskHandler taskHandler;

    /**
     * 1、根据id查询课程记录
     *
     * @param courseId
     * @return
     */
    @Override
    public LearningLessonDTO queryLearningRecordByCourse(Long courseId) {

        // 1、根据courseId和userId共同查询出课表
        LearningLesson lesson = this.lessonService.lambdaQuery()
                .eq(LearningLesson::getCourseId, courseId)
                .eq(LearningLesson::getUserId, UserContext.getUser())
                .one();
        // 1、判断应该新增记录，还是应该更新记录

        // 2、根据课表id查询记录表中的所有记录
        List<LearningRecord> list = this.lambdaQuery()
                .eq(LearningRecord::getLessonId, lesson.getId())
                .list();

        // 3、拼装LearningLessonDTO并返回
        return LearningLessonDTO.of(lesson.getId(), lesson.getLatestSectionId(), BeanUtils.copyList(list, LearningRecordDTO.class));

    }

    /**
     * 2、提交学习记录（Day3 - 随堂2）
     *
     * @param formDTO 课程id
     * @return
     */
    @Override
    @Transactional
    public void submitLearningRecord(LearningRecordFormDTO formDTO) {
        // 1、处理学习记录
        boolean firstFinished = formDTO.getSectionType() == VIDEO ? handleVideoRecord(formDTO) : handleExamRecord(formDTO);

        // 2、处理课表数据
        handleLearningLesson(formDTO, firstFinished);
    }

    /**
     * 处理考试学习记录
     *
     * @param formDTO 学习记录
     * @return
     */
    private boolean handleExamRecord(LearningRecordFormDTO formDTO) {
        // 新增学习记录
        LearningRecord lr = BeanUtils.copyBean(formDTO, LearningRecord.class);
        lr.setUserId(UserContext.getUser());
        lr.setFinished(true);
        lr.setFinishTime(LocalDateTime.now());
        try {
            save(lr);
        } catch (Exception e) {
            throw new DbException("新增学习记录失败");
        }
        return true;
    }


    /**
     * 处理视频学习记录
     *
     * @param formDTO 学习记录
     * @return
     */
    private boolean handleVideoRecord(LearningRecordFormDTO formDTO) {
        // 1、判断应该新增记录，还是应该更新记录
        // 直接先查询redis,缓存中查不到，再去查数据库
        LearningRecord oldRecord = queryRecord(formDTO);

        if (ObjectUtil.isEmpty(oldRecord)) {
            LearningRecord lr = BeanUtils.copyBean(formDTO, LearningRecord.class);
            lr.setUserId(UserContext.getUser());
            lr.setFinishTime(LocalDateTime.now());
            lr.setFinished(false);
            save(lr);
            // 新增完记录后，还要保存到redis
            // 需要注意这里必须有主键回填，不然后续没法以redis做延迟更新
            taskHandler.writeToCacheAndSubmitTask(lr);
            return false;
        }

        // 2、更新学习记录
        // 2.1、判断是否是第一次完成
        boolean isFirstFinished = !oldRecord.getFinished() && formDTO.getMoment() * 2 > formDTO.getDuration();

        if (isFirstFinished) {
            this.lambdaUpdate()
                    .eq(LearningRecord::getLessonId, formDTO.getLessonId())
                    .eq(LearningRecord::getSectionId, formDTO.getSectionId())
                    .set(LearningRecord::getFinished, true)
                    .set(LearningRecord::getMoment, formDTO.getMoment())
                    .set(LearningRecord::getFinishTime, formDTO.getCommitTime())
                    .update();

            // 直接删除redis缓存
            taskHandler.deleteCache(formDTO.getLessonId(), formDTO.getSectionId());
        } else {
            // 缓存到 redis 并提交延迟检测任务
            LearningRecord lr = BeanUtils.copyBean(formDTO, LearningRecord.class);
            // id 缓存到学习记录表
            lr.setId(oldRecord.getId());
            lr.setFinished(oldRecord.getFinished());
            lr.setFinishTime(oldRecord.getFinishTime());
            // 这里没有主键回填
            taskHandler.writeToCacheAndSubmitTask(lr);
        }

        return isFirstFinished;
    }

    private LearningRecord queryRecord(LearningRecordFormDTO formDTO) {
        LearningRecord learningRecord = LearningRecord.of();
        learningRecord = taskHandler.readLearningRecordFromCache(formDTO.getLessonId(), formDTO.getSectionId());

        if (ObjectUtils.isNotEmpty(learningRecord)) {
            // 直接从 redis 中拿
            return learningRecord;
        }
        learningRecord = this.lambdaQuery()
                .eq(LearningRecord::getLessonId, formDTO.getLessonId())
                .eq(LearningRecord::getSectionId, formDTO.getSectionId())
                .one();

        if (ObjectUtils.isNotEmpty(learningRecord)) {
            taskHandler.writeToCacheAndSubmitTask(learningRecord);
        }

        return learningRecord;
    }


    private void updateLatest(LearningRecordFormDTO formDTO) {
        // 更新课表最近学习时间和最近学习小节
        this.lessonService.lambdaUpdate()
                .eq(LearningLesson::getId, formDTO.getLessonId())
                .set(LearningLesson::getLatestLearnTime, LocalDateTime.now())
                .set(LearningLesson::getLatestSectionId, formDTO.getSectionId())
                .update();
    }

    /**
     * 处理课表数据
     *
     * @param formDTO       学习记录
     * @param firstFinished 是否是第一次学完该小节
     * @return
     */
    private void handleLearningLesson(LearningRecordFormDTO formDTO, boolean firstFinished) {
        // 2.2、确实是第一次完成，新增学习小节数
        updateLatest(formDTO);
        this.lessonService.lambdaUpdate()
                .eq(LearningLesson::getId, formDTO.getLessonId())
                .setSql("learned_sections = learned_sections + 1")
                .update();

        // 3、判断该课表的所有小节是否已经全部完成
        LearningLesson ll = this.lessonService.lambdaQuery().eq(LearningLesson::getId, formDTO.getLessonId()).one();
        CourseFullInfoDTO courseInfoById = courseClient.getCourseInfoById(ll.getCourseId(), false, false);

        if (ll.getLearnedSections() >= courseInfoById.getSectionNum()) {
            this.lessonService.lambdaUpdate()
                    .set(LearningLesson::getStatus, FINISHED)
                    .update();
        }
    }



}
